package GUI;

import java.awt.Color;
import java.awt.Dimension;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.Toolkit;
import java.awt.event.ActionListener;
import java.util.Observable;
import java.util.Observer;

import javax.swing.BorderFactory;
import javax.swing.BoxLayout;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTable;
import javax.swing.ListSelectionModel;
import javax.swing.SwingConstants;
import javax.swing.border.BevelBorder;

import lejos.pc.comm.NXTConnectionState;
import lejos.pc.comm.NXTInfo;
import Controllers.ActionCommand;
import Controllers.TableSelectionController;

/**
 * Main GUI class implementing the Observer interface for using in the Observer
 * Pattern. This is also the view part of the MVC architecture used for the
 * application.
 * 
 * @author Danny Johansen, Didzis Petkus, Liviu Berheci.
 * 
 */
public class MapperMain extends JFrame implements Observer {

	private static final long serialVersionUID = 2606910997501261834L;
	private JPanel leftPanel;
	private JPanel rightPanel;
	private JPanel mainPanel;
	private JPanel buttonsPanel;
	private JButton searchBtn;
	private JButton stopSearchBtn;
	private JButton connectBtn;
	private JLabel statusLabel;

	private JScrollPane scroll;

	private JTable nxtTable;
	private NXTTableModel tableModel;
	private NXTInfo[] nxts;

	/**
	 * Constructor taking a title.
	 * @param title - a string used for setting the title of the JFrame.
	 */
	public MapperMain(String title) {
		setTitle(title);
		Dimension screen = new Dimension(Toolkit.getDefaultToolkit()
				.getScreenSize());
		Dimension frame = new Dimension((int) (screen.getWidth() * 0.35),
				(int) (screen.getHeight() * 0.20));

		setSize(frame);
		setMinimumSize(frame);
		setPreferredSize(frame);
		setLocation(3, 3);
		setVisible(true);

		mainPanel = new JPanel(new GridBagLayout());
		// mainPanel.setBackground(Color.BLACK);

		add(mainPanel);

		leftPanel = new JPanel(new GridBagLayout());
		// leftPanel.setBackground(Color.YELLOW);
		rightPanel = new JPanel(new GridBagLayout());
		// rightPanel.setBackground(Color.BLUE);

		GridBagConstraints c = new GridBagConstraints();

		c.gridx = 1;
		c.gridy = 0;
		c.gridwidth = 1;
		c.gridheight = 1;
		c.weightx = 0.8;
		c.weighty = 1.0;
		c.fill = GridBagConstraints.BOTH;
		// c.anchor = GridBagConstraints.FIRST_LINE_START;
		mainPanel.add(leftPanel, c);

		c.gridx = 2;
		c.gridy = 0;
		c.gridwidth = 1;
		c.gridheight = 1;
		c.weightx = 0;
		c.weighty = 1.0;
		c.fill = GridBagConstraints.VERTICAL;
		c.anchor = GridBagConstraints.FIRST_LINE_START;
		rightPanel.setBorder(BorderFactory.createEtchedBorder(Color.lightGray,
				rightPanel.getBackground()));
		mainPanel.add(rightPanel, c);

		JPanel statusPanel = new JPanel();
		statusPanel.setBorder(new BevelBorder(BevelBorder.LOWERED));
		statusPanel.setLayout(new BoxLayout(statusPanel, BoxLayout.X_AXIS));
		c.gridx = 0;
		c.gridy = 1;
		c.gridwidth = GridBagConstraints.REMAINDER;
		c.gridheight = 1;
		c.weightx = 0;
		c.weighty = 0;
		c.fill = GridBagConstraints.HORIZONTAL;
		c.anchor = GridBagConstraints.PAGE_END;
		mainPanel.add(statusPanel, c);

		statusLabel = new JLabel("This is status label!");
		statusLabel.setHorizontalAlignment(SwingConstants.LEFT);
		statusPanel.add(statusLabel);

		tableModel = new NXTTableModel(null);
		nxtTable = new JTable(tableModel);
		nxtTable.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
		nxtTable.getTableHeader().setReorderingAllowed(false);

		scroll = new JScrollPane(nxtTable);
		c.gridx = 0;
		c.gridy = 0;
		c.gridwidth = 1;
		c.gridheight = 1;
		c.weightx = 1;
		c.weighty = 1.0;
		c.fill = GridBagConstraints.BOTH;
		leftPanel.setBorder(BorderFactory.createEtchedBorder(Color.lightGray,
				leftPanel.getBackground()));
		leftPanel.add(scroll, c);

		buttonsPanel = new JPanel(new GridBagLayout());
		c = new GridBagConstraints();
		c.gridx = 0;
		c.gridy = 0;
		c.gridwidth = 1;
		c.gridheight = 1;
		c.weightx = 1;
		c.weighty = 1;
		c.anchor = GridBagConstraints.FIRST_LINE_START;
		buttonsPanel.setBorder(BorderFactory.createEtchedBorder(
				Color.lightGray, buttonsPanel.getBackground()));

		rightPanel.add(buttonsPanel, c);

		searchBtn = new JButton("Search");
		c.gridx = 0;
		c.gridy = 0;
		c.gridwidth = 1;
		c.gridheight = 1;
		c.weightx = 1;
		c.weighty = 0;
		buttonsPanel.add(searchBtn, c);

		stopSearchBtn = new JButton("Stop Search");
		c.gridx = 1;
		c.gridy = 0;
		c.gridwidth = 1;
		c.gridheight = 1;
		c.weightx = 1;
		c.weighty = 0;
		buttonsPanel.add(stopSearchBtn, c);

		connectBtn = new JButton("Connect");
		connectBtn.setEnabled(false);
		c.gridx = 0;
		c.gridy = 1;
		c.gridwidth = 2;
		c.gridheight = 1;
		c.weightx = 1;
		c.weighty = 0;
		c.fill = GridBagConstraints.HORIZONTAL;
		buttonsPanel.add(connectBtn, c);

		enableSearch(true);

		searchBtn.setActionCommand(ActionCommand.SEARCH.toString());
		stopSearchBtn.setActionCommand(ActionCommand.STOP_SEARCH.toString());
		connectBtn.setActionCommand(ActionCommand.CONNECT.toString());

		pack();
	}

	@Override
	public void update(Observable o, Object data) {
		if (data instanceof NXTInfo[]) {
			NXTInfo[] nxtInfo = (NXTInfo[]) data;
			updateTable(nxtInfo);
		}
		if (data instanceof NXTInfo) {
			NXTInfo newInfo = (NXTInfo) data;

			for (NXTInfo info : nxts) {
				if (newInfo.deviceAddress == info.deviceAddress) {
					info.connectionState = newInfo.connectionState;
					break;
				}
			}
			updateTable(nxts);
		}
	}

	/**
	 * Updates a table model with the given data.
	 * @param nxtInfo - a NXTInfo array collection with data to update a
	 * table model with.
	 */
	private void updateTable(NXTInfo[] nxtInfo) {
		this.nxts = nxtInfo;
		if (nxtInfo.length == 0) {
			updateStatus("No NXTs found.   ");
		}
		tableModel.update(nxtInfo);
	}

	/**
	 * Adds listeners to the controller for components in this frame.
	 * @param controller - the passed controller object in a MVC architecture.
	 */
	public void assignController(ActionListener controller) {
		searchBtn.addActionListener(controller);
		stopSearchBtn.addActionListener(controller);
		connectBtn.addActionListener(controller);
	}

	/**
	 * Returns a NXTInfo object from an array of NXTInfos corresponding
	 * to a table entry selected.
	 * @return - lejos.pc.comm.NXTInfo
	 */
	public NXTInfo getSelectedNXT() {
		int selection = nxtTable.getSelectedRow();
		return nxts[selection];
	}

	/**
	 * Displays an exception message in a message dialogue frame.
	 * @param e - the passed Exception to extract the message from.
	 */
	public void showError(Exception e) {
		JOptionPane.showMessageDialog(this, e.getMessage(), "Error!",
				JOptionPane.ERROR_MESSAGE);
		updateStatus("");
	}

	/**
	 * Updates the statusbar label with the passed text.
	 * @param message - a String to set the label text to. 
	 */
	public void updateStatus(String message) {
		statusLabel.setText(message);
	}

	/**
	 * Enables/disables the search and stop search buttons using a boolean value.
	 * @param enabled - a boolean value passed to set the buttons' enabled state.
	 */
	public void enableSearch(boolean enabled) {
		searchBtn.setEnabled(enabled);
		stopSearchBtn.setEnabled(!enabled);
	}

	/**
	 * Toggles the connect button's text to be either connect/disconnect
	 * and state to be enabled/disabled according to the enabled value.
	 * @param enabled - boolean value for setting the button's enabled state.
	 */
	public void toggleConnectBtn(boolean enabled) {
		connectBtn.setEnabled(enabled);
		if (nxtTable.getSelectedRow() != -1) {
			int row = nxtTable.getSelectedRow();

			NXTConnectionState selectedNXT = nxts[row].connectionState;
			connectBtn
					.setText(selectedNXT != NXTConnectionState.LCP_CONNECTED ? "Connect"
							: "Disconnect");
		}
	}

	/**
	 * Registers a listener on the controller object for handling
	 * table selection events.  
	 * @param controller - the controller object used in a MVC architecture.
	 */
	public void addTableSelectionListener(TableSelectionController controller) {
		nxtTable.getSelectionModel().addListSelectionListener(controller);
	}

}
